package com.openbravo.data.loader.dialect;


import com.openbravo.data.loader.sqlbuilder.QBFBuilder;
import e.odbo.data.E;
import e.odbo.data.dsl.operation.Order;
import e.odbo.data.dsl.operation.Page;
import e.odbo.data.dsl.operation.QueryOperation;
import e.odbo.data.dsl.I_ColumnFunction;
import e.odbo.data.dsl.Join;
import e.odbo.data.dsl.Select;
import e.odbo.data.dsl.func.FORMATFunction;
import e.odbo.data.dsl.func.MIDFunction;
import e.odbo.data.dsl.func.ROUNDFunction;

import java.util.List;
import java.util.Map;

public abstract  class AbstractDBDialect implements SessionDialect<String> {

    @Override
    public String formatPage(Page page) {
        if (page.pageNum> 0) {
            return " limit " + page.pageNum*page.pageSize + "," + page.pageSize;
        }
        return " limit " + page.pageSize;

    }
    AbstractDDLFactory ddlFactory;

    public  AbstractDDLFactory getDDLFactory(){
        if(ddlFactory==null)
            ddlFactory=createDDLFactory();
        return ddlFactory;
    }

    public abstract AbstractDDLFactory createDDLFactory();

    public String getSQL(I_ColumnFunction function){
        StringBuilder retString=new StringBuilder();
        switch (function.typeName()){
            case E.DB.FUNC.AVG:
            case E.DB.FUNC.COUNT:
            case E.DB.FUNC.FIRST:
            case E.DB.FUNC.LAST:
            case E.DB.FUNC.LCASE:
            case E.DB.FUNC.MAX:
            case E.DB.FUNC.MIN:
            case E.DB.FUNC.SUM:
            case E.DB.FUNC.UCASE:
            case E.DB.FUNC.LEN:
                retString.append(function.typeName()+"("+ function.getColumnName()+")");

                break;
            case E.DB.FUNC.NOW:
                retString.append(" NOW() ");
                break;
            case E.DB.FUNC.FORMAT:
                FORMATFunction formatFunction=(FORMATFunction)function;
                retString.append(function.typeName()+" ("+ function.getColumnName()+","+formatFunction.getFormat()+") ");
                break;
            case E.DB.FUNC.MID:
                MIDFunction midFunction=(MIDFunction)function;
                if(midFunction.getLength()==-1)
                    retString.append(function.typeName()+" ("+ function.getColumnName()+","+midFunction.getStart()+") ");
                else
                    retString.append(function.typeName()+" ("+ function.getColumnName()+","+midFunction.getStart()+","+midFunction.getLength()+") ");
                break;
            case E.DB.FUNC.ROUND:
                ROUNDFunction roundFunction=(ROUNDFunction)function;
                retString.append(function.typeName()+" ("+ function.getColumnName()+","+roundFunction.getDecimals()+") ");
                break;
            default:
                retString.append(function.getColumnName());
        }
        retString.append(function.getAlise()==null?"":" AS "+function.getAlise()+" ");
        return retString.toString();
    }

    public  String getSQL(Select select){
        StringBuilder sql=new StringBuilder("SELECT ");
        if(select.isDistinct())
            sql.append(" DISTINCT ");

        //COLUMN
        boolean firstColumn=true;
        if(select.getSelectColumns()!=null){
            for(Object column:select.getSelectColumns()){
                if(!firstColumn)
                    sql.append(",");
                if(column instanceof I_ColumnFunction){
                    sql.append(getSQL((I_ColumnFunction)column));
                }else
                    sql.append(column);
                firstColumn=false;
            }
        }else
            sql.append(" * ");

        //TABLE
        firstColumn=true;
        for(Select.Table table:select.getTables()){
            if(!firstColumn)
                sql.append(",");
            else
                sql.append(" FROM ");
            sql.append(getSQL(table));
            firstColumn=false;
        }

        //JOIN
        if(!select.getJoins().isEmpty())
            for(Join tableJoin:select.getJoins()){
                sql.append(getSQL(tableJoin));
            }

        //QBF
        if(null!=select.getQbfParameters())
            sql.append(" WHERE "+ QBFBuilder.getFilterSQL(select.getQbfParameters()));


        //GROUP
        if(null!=select.getGroups()){
            firstColumn=true;
            sql.append(" GROUP BY ");
            for(Object column:select.getGroups()){
                if(!firstColumn)
                    sql.append(",");
                sql.append(column);
                firstColumn=false;
            }
            if(null!=select.getHaving())
                sql.append(" HAVING ").append(QBFBuilder.getFilterSQL(select.getHaving()));
        }

        //QOP
        sql.append(getSQL(select.getQueryOperation()));

        //UNION
        if(null!=select.getUnionSelect())
            sql.append(getSQL(select.getUnionSelect()));

        return sql.toString();
    }


    public  String getSQL(Select.Table table){
        String ret=null;
        if(table.getSubSelect()!=null)
            ret= " ("+getSQL(table.getSubSelect())+") ";
        else
            ret=table.getTableName();
        if(null!=table.getAlise())
            ret=ret+" "+table.getAlise()+" ";
        return ret;
    }

    public  String getSQL(Join join){
        boolean firstColumn=true;
        String ret=" "+join.getJoinType().getName()+" "+join.getTable()+" ON (";
        for(Map.Entry qbf:join.getQbf().entrySet()){
            if(!firstColumn)
                ret=ret+",";
            ret=ret+qbf.getKey()+"="+qbf.getValue();
            firstColumn=false;
        }

        return ret+") ";
    }


    public  String getSQL(QueryOperation operation){
        return getSQL(operation,null);
    }

    public  String getSQL(QueryOperation operation, String alise){
        StringBuilder retStr=new StringBuilder();
        if(!operation.orders.isEmpty())
            retStr.append(getSQL(operation.orders,alise));
        if(operation.page!=null)
            retStr.append(getSQL(operation.page));
        return retStr.toString();
    }


    public  String getSQL(List<Order> orders){
        return getSQL(orders,null);
    }

    public  String getSQL(List<Order> orders, String alise){
        StringBuilder retStr=new StringBuilder();

        retStr.append(" order by ");
        boolean more=false;
        for(Order order:orders){
            if(more)
                retStr.append(",");
            retStr.append(alise==null?order.getFieldName():alise+"."+order.getFieldName());
            if(!order.isAsc())
                retStr.append(" desc ");
            more=true;
        }
        return retStr.toString();
    }

    public  String getSQL(Page page){
        return formatPage(page);
    }

}
